﻿// Copyright (c) 2012 Mokah Nnamezie & Andrew Cooper
// Neo4Net graph database - http://neo4net.codeplex.com
//
// This file is part of Neo4Net - a .Net port of the Neo4j graph database
//
// Neo4Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace Neo4Net.Kernel.Impl.Core
{
    using System;
    using System.Threading;
    using Neo4Net.GraphDb;
    using NUnit.Framework;

    [TestFixture]
    public class TestIsolation : AbstractNeo4NetTestCase
    {
        [Test]
        public void TestSimpleTransactionIsolation()
        {
            Commit();
            CountdownEvent latch1 = new CountdownEvent(1);
            CountdownEvent latch2 = new CountdownEvent(1);
            ITransaction tx = GraphDb.BeginTx();
            INode n1, n2;
            IRelationship r1;
            try
            {
                n1 = GraphDb.CreateNode();
                n2 = GraphDb.CreateNode();
                r1 = n1.CreateRelationshipTo(n2, DynamicRelationshipType.WithName("TEST"));
                tx.Success();
            }
            finally
            {
                tx.Finish();
            }
            INode node1 = n1;
            INode node2 = n2;
            IRelationship rel1 = r1;
            tx = GraphDb.BeginTx();
            try
            {
                node1.SetProperty("key", "old");
                rel1.SetProperty("key", "old");
                tx.Success();
            }
            finally
            {
                tx.Finish();
            }
            AssertPropertyEqual(node1, "key", "old");
            AssertPropertyEqual(rel1, "key", "old");
            AssertRelationshipCount(node1, 1);
            AssertRelationshipCount(node2, 1);
            Thread t1 = new Thread(() =>
            {
                ITransaction transaction = GraphDb.BeginTx();
                try
                {
                    node1.SetProperty("key", "new");
                    rel1.SetProperty("key", "new");
                    node1.CreateRelationshipTo(node2, DynamicRelationshipType.WithName("TEST"));
                    AssertPropertyEqual(node1, "key", "new");
                    AssertPropertyEqual(rel1, "key", "new");
                    AssertRelationshipCount(node1, 2);
                    AssertRelationshipCount(node2, 2);
                    latch1.Signal();
                    latch2.Wait();
                    AssertPropertyEqual(node1, "key", "new");
                    AssertPropertyEqual(rel1, "key", "new");
                    AssertRelationshipCount(node1, 2);
                    AssertRelationshipCount(node2, 2);
                    // no tx.success();
                }
                catch (ThreadInterruptedException e)
                {
                    Console.Error.WriteLine(e);
                }
                finally
                {
                    transaction.Finish();
                    AssertPropertyEqual(node1, "key", "old");
                    AssertPropertyEqual(rel1, "key", "old");
                    AssertRelationshipCount(node1, 1);
                    AssertRelationshipCount(node2, 1);
                }
            });
            t1.Start();
            try
            {
                latch1.Wait();
            }
            catch (ThreadInterruptedException e)
            {
            }

            AssertPropertyEqual(node1, "key", "old");
            AssertPropertyEqual(rel1, "key", "old");
            AssertRelationshipCount(node1, 1);
            AssertRelationshipCount(node2, 1);

            latch2.Signal();
            try
            {
                t1.Join();
            }
            catch (ThreadInterruptedException e)
            {
            }
            AssertPropertyEqual(node1, "key", "old");
            AssertPropertyEqual(rel1, "key", "old");
            AssertRelationshipCount(node1, 1);
            AssertRelationshipCount(node2, 1);

            tx = GraphDb.BeginTx();
            try
            {
                foreach (IRelationship rel in node1.GetRelationships())
                {
                    rel.Delete();
                }
                node1.Delete();
                node2.Delete();
                tx.Success();
            }
            finally
            {
                tx.Finish();
            }
        }

        private void AssertPropertyEqual(IPropertyContainer primitive, String key,
            String value)
        {
            ITransaction tx = GraphDb.BeginTx();
            try
            {
                Assert.That(primitive.GetProperty(key), Is.EqualTo(value));
            }
            finally
            {
                tx.Finish();
            }
        }

        private void AssertRelationshipCount(INode node, int count)
        {
            ITransaction tx = GraphDb.BeginTx();
            try
            {
                int actualCount = 0;
                foreach (IRelationship rel in node.GetRelationships())
                {
                    actualCount++;
                }
                Assert.That(actualCount, Is.EqualTo(count));
            }
            finally
            {
                tx.Finish();
            }
        }
    }
}
